/*
 * Copyright (c) 20202099 the original author or authors. All rights reserve.
 */
package org.tianyun.cloud.security.server;

import reactor.core.publisher.Mono;

import java.util.List;

/**
 * 默认过滤器链实现
 *
 * @auther ebert_chan
 */
public class DefaultSecurityFilterChain<T, U> implements SecurityFilterChain<T, U> {

    private final List<SecurityFilter<T, U>> securityFilters;

    public DefaultSecurityFilterChain(List<SecurityFilter<T, U>> securityFilters) {
        this.securityFilters = securityFilters;
    }

    /*
     * @see org.tianyun.cloud.security.server.SecurityFilterChain#doFilter(org.tianyun.cloud.security.server.SecurityWebExchange)
     */
    @Override
    public Mono<Void> doFilter(SecurityWebExchange<T, U> exchange) {
        if (securityFilters.isEmpty()) {
            return Mono.empty();
        }
        SecurityFilterChainProxy<T, U> proxy = new SecurityFilterChainProxy<T, U>(this);
        try {
            proxy.doFilter(exchange);
            if (proxy.isComplete()) {
                return Mono.empty();
            }
        } catch (Throwable e) {
            return Mono.error(e);
        }
        return null;
    }

    static class SecurityFilterChainProxy<T, U> implements SecurityFilterChain<T, U> {

        private DefaultSecurityFilterChain<T, U> chain;

        private int index;

        public SecurityFilterChainProxy(DefaultSecurityFilterChain<T, U> chain) {
            this.chain = chain;
        }

        /*
         * @see org.tianyun.cloud.security.server.SecurityFilterChain#doFilter(org.tianyun.cloud.security.server.SecurityWebExchange)
         */
        @Override
        public Mono<Void> doFilter(SecurityWebExchange<T, U> exchange) {
            if (!isComplete()) {
                SecurityFilter<T, U> f = chain.securityFilters.get(index++);
                f.filter(exchange, this);
            }
            return null;
        }

        public boolean isComplete() {
            return index >= chain.securityFilters.size();
        }

    }

}
